/* Copyright Thanh-Trung Pham, JAIST, 2013 */

package jp.ac.jaist.cli;

import java.io.IOException;
import java.util.List;
import jp.ac.jaist.FailureTypeType;
import jp.ac.jaist.computation.RMPReliabilityCalculator;
import jp.ac.jaist.message.RMPCLIException;
import jp.ac.jaist.message.RMPInputModelException;
import jp.ac.jaist.message.RMPMessageResource;
import jp.ac.jaist.input.RMPInputModelManager;
import jp.ac.jaist.input.RMPInternalActivityWrapper;
import org.apache.commons.cli.BasicParser;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.OptionGroup;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.xmlbeans.XmlError;

/**
 *
 * @author Thanh-Trung Pham
 */
public class RMPReliabilityModelingPrediction {

    private static final String USAGE = "[-h] [-v] [-p] <input_model_file> <output_file>";
    private static final String HEADER = "SRP - A reliability prediction tool, Copyright 2013 Thanh-Trung Pham";
    private static final String FOOTER = "For more instructions, contact us at: thanhtrung.pham@outlook.com";
    private String m_oCommandLineArgs[];

    /**
     * The constructor.
     * @param commandLineArgs an array of command line arguments.
     */
    public RMPReliabilityModelingPrediction(String[] commandLineArgs) {
        m_oCommandLineArgs = commandLineArgs;
    }

    /**
     * Start the reliability modeling tool.
     */
    private void doStart() {
        OptionGroup oOptionGroup = new OptionGroup();
        oOptionGroup.addOption(OptionBuilder.hasArg(false).withDescription("Print the usage information").create('h'));
        oOptionGroup.addOption(OptionBuilder.hasArg(false).withDescription("Only conduct a validation on the input model").create('v'));
        oOptionGroup.addOption(OptionBuilder.hasArg(false).withDescription("Only conduct a prediction on the input model").create('p'));

        CommandLineParser oParser = new BasicParser();
        Options oOptions = new Options();
        oOptions.addOptionGroup(oOptionGroup);

        try {
            CommandLine oCommandLine = oParser.parse(oOptions, m_oCommandLineArgs);
            if (oCommandLine.hasOption('h')) {
                printUsageInformation(oOptions);
                return;
            }

            String[] args = oCommandLine.getArgs();
            if (args == null || args.length != 2 || StringUtils.isBlank(args[0]) || StringUtils.isBlank(args[1])) {
                printInformation("Bad command line arguments were provided!\n", false);
                printUsageInformation(oOptions);
                return;
            }

            String strInputModelFilename = args[0];
            String strOutputFilename = args[1];

            StringBuilder oOutputContent = new StringBuilder();

            boolean isValidInputModel = validateInputReliabilityModeling(strInputModelFilename, oOutputContent);
            if (!isValidInputModel) {
                IOUtils.write(oOutputContent.toString(), new java.io.FileOutputStream(strOutputFilename));
                return;
            }

            if (oCommandLine.hasOption('v')) {
                IOUtils.write(oOutputContent.toString(), new java.io.FileOutputStream(strOutputFilename));
                return;
            }

            printInformation("Conducting a reliabilty prediction... ", true);
            conductReliabilityPrediction(oOutputContent);

            IOUtils.write(oOutputContent.toString(), new java.io.FileOutputStream(strOutputFilename));

            printInformation("Done.", false);
            printInformation("Check the output file for detailed results.", true);

            if (oCommandLine.hasOption('p')) {
                return;
            }
        } catch (IOException oIOException) {
            RMPCLIException oRMCLIException = new RMPCLIException(RMPMessageResource.CLI_UNEXPECTED_EXCEPTION, oIOException, false);
            printErrorInformation(oRMCLIException);
        } catch (ParseException oParseException) {
            printErrorInformation(oParseException);
            printUsageInformation(oOptions);
        }
    }

    /**
     * Print the error information.
     * @param exception the exception caused the error.
     */
    private void printErrorInformation(Exception exception) {
        System.out.println(exception.getMessage());
    }

    /**
     * The main method.
     * @param args an array of command line arguments.
     */
    public static void main(String args[]) {
        RMPReliabilityModelingPrediction oModeling = new RMPReliabilityModelingPrediction(args);
        oModeling.doStart();
    }

    /**
     * Print the usage information.
     * @param oOptions the command line options.
     */
    private void printUsageInformation(Options oOptions) {
        HelpFormatter oHelpFormatter = new HelpFormatter();
        oHelpFormatter.setWidth(80);
        oHelpFormatter.printHelp(USAGE, HEADER, oOptions, FOOTER);
    }

    /**
     * Print information.
     * @param information information to print.
     * @param isOnNewLine if true, the information will be on a new line, otherwise false.
     */
    private void printInformation(String information, boolean isOnNewLine) {
        if (isOnNewLine) {
            System.out.println();
        }

        System.out.print(information);
    }

    /**
     * Validate the input model.
     * @param strInputModelFilename the input model file name.
     * @param outputContent the output content.
     * @return true if the input model is valid, otherwise false.
     */
    private boolean validateInputReliabilityModeling(String strInputModelFilename, StringBuilder outputContent) {
        printInformation("Validating the input model... ", false);
        RMPInputModelManager oManager = RMPInputModelManager.getInstance();
        try {
            oManager.setInputModelFilename(strInputModelFilename);
            outputContent.append("# The input model is valid.\n");
            printInformation("Done.", false);
            printInformation("The input model is valid.", true);

            return true;
        } catch (RMPInputModelException oRMInputModelException) {
            outputContent.append(oRMInputModelException.getMessage()).append("\n");
            List<XmlError> oXMLErrors = oManager.getXMLErrors();
            if (oXMLErrors != null) {
                for (XmlError oXMLError : oXMLErrors) {
                    outputContent.append("Line ").append(oXMLError.getLine()).append(": ").append(oXMLError.getMessage()).append("\n");
                }
            }

            printInformation("Done.", false);
            printInformation("The input model is not valid. Check the output file for more details!", true);
            return false;
        }
    }

    /**
     * Conduct a reliability prediction for a user interface.
     * @param outputContent the output content.
     */
    private void conductReliabilityPrediction(StringBuilder outputContent) {
        RMPReliabilityCalculator oCalculator = RMPReliabilityCalculator.getInstance();

        RMPInternalActivityWrapper oIAWrapper = oCalculator.calculateReliabilityRelatedProbabilities();

        outputContent.append("#====================================================================\n");
        outputContent.append("# Predicted reliability: ").append(oIAWrapper.getSp()).append("\n");

        for (FailureTypeType oFailureType : RMPInputModelManager.getInstance().getFailureTypes()) {
            String strFailureTypeName = oFailureType.getName();
            outputContent.append("# Predicted failure probability of '").append(strFailureTypeName).append("': ").append(oIAWrapper.getFp(strFailureTypeName)).append("\n");
        }
    }
}
